#include "Model.h"
#include "Util.h"

#include <fstream>


namespace TestDX
{

Model::Model(void) :
	_pVertexBuffer(NULL),
	_pIndexBuffer(NULL),
	_vertexCount(0),
	_indexCount(0),
	_pModel(NULL),
	_pTexture(NULL)
{
}


Model::~Model(void)
{
	Shutdown();
}


bool Model::Init(ID3D11Device * device,
				 WCHAR * modelFilename,
				 WCHAR * textureFilename)
{
	if(!LoadModel(modelFilename))
	{
		// @ TODO: Log
		return false;
	}

	if(!LoadTexture(device, textureFilename))
	{
		// @ TODO: Log
		return false;
	}

	return InitBuffers(device);
}

void Model::Shutdown()
{
	ShutdownBuffers();
	ReleaseTexture();
	ReleaseModel();
}

void Model::Render(ID3D11DeviceContext * deviceContext)
{
	RenderBuffers(deviceContext);
}

bool Model::InitBuffers(ID3D11Device * device)
{
	Vertex * vertices = new Vertex[_vertexCount];
	if(!vertices)
	{
		return false;
	}

	Index * indices = new Index[_indexCount];
	if(!indices)
	{
		return false;
	}

	// Load data from model (that was previously loaded from file)
	for(int i = 0; i < _vertexCount; ++i)
	{
		vertices[i].position = XMFLOAT3(_pModel[i].x,
									    _pModel[i].y,
									    _pModel[i].z);
		vertices[i].texture = XMFLOAT2(_pModel[i].tu,
									   _pModel[i].tv);
		vertices[i].normal = XMFLOAT3(_pModel[i].nx,
									  _pModel[i].ny,
									  _pModel[i].nz);
		indices[i] = i;
	}

	// Set up description of static vertex buffer
	D3D11_BUFFER_DESC vbd;
	ZeroMemory(&vbd, sizeof(vbd));

	vbd.ByteWidth = sizeof(Vertex) * _vertexCount;
	vbd.Usage = D3D11_USAGE_IMMUTABLE;
	vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	vbd.CPUAccessFlags = 0;
	vbd.MiscFlags = 0;
	vbd.StructureByteStride = 0;

	D3D11_SUBRESOURCE_DATA vertexData;
	ZeroMemory(&vertexData, sizeof(vertexData));
	vertexData.pSysMem = vertices;
	
	HR(device->CreateBuffer(&vbd, &vertexData, &_pVertexBuffer));

	D3D11_BUFFER_DESC ibd;
	ZeroMemory(&ibd, sizeof(ibd));

	ibd.ByteWidth = sizeof(unsigned long) * _indexCount;
	ibd.Usage = D3D11_USAGE_IMMUTABLE;
	ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;

	D3D11_SUBRESOURCE_DATA indexData;
	ZeroMemory(&indexData, sizeof(indexData));
	indexData.pSysMem = indices;

	HR(device->CreateBuffer(&ibd, &indexData, &_pIndexBuffer));

	// @ TODO: No se hace delete si falla HR
	SAFE_DELETE_V(vertices);
	SAFE_DELETE_V(indices);

	return true;
}

void Model::ShutdownBuffers()
{
	RELEASE_COM(_pIndexBuffer);
	RELEASE_COM(_pVertexBuffer);
}

void Model::RenderBuffers(ID3D11DeviceContext * deviceContext)
{
	unsigned int stride = sizeof(Vertex), offset = 0;
	
	deviceContext->IASetVertexBuffers(0,
									  1,
									  &_pVertexBuffer,
									  &stride,
									  &offset);


	deviceContext->IASetIndexBuffer(_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0);

	deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}

bool Model::LoadModel(WCHAR * filename)
{
	std::ifstream fin(filename);

	if(fin.fail())
	{
		// @ TODO: Log
		return false;
	}

	// With this file format, we have same number of vertices and indices (sad!)
	fin >> _vertexCount;
	_indexCount = _vertexCount;

	_pModel = new TModel[_vertexCount];
	if(!_pModel)
	{
		// @ TODO: Log
		return false;
	}

	for(int i = 0; i < _vertexCount; ++i)
	{
		fin >> _pModel[i].x >> _pModel[i].y >> _pModel[i].z;
		fin >> _pModel[i].tu >> _pModel[i].tv;
		fin >> _pModel[i].nx >> _pModel[i].ny >> _pModel[i].nz;
		// @ TODO: Some safety please. What if there are less lines than expected?
	}

	fin.close();

	return true;
}

void Model::ReleaseModel()
{
	SAFE_DELETE_V(_pModel);
}

bool Model::LoadTexture(ID3D11Device * device, WCHAR * filename)
{
	_pTexture = new Texture;
	if(!_pTexture)
	{
		// @ TODO: Log
		return false;
	}

	return _pTexture->Init(device, filename);
}

void Model::ReleaseTexture()
{
	SAFE_DELETE(_pTexture);
}

};